
;;;======================================================
;;;   Sticks Program
;;;
;;;     This program was introduced in Chapter 9.
;;;
;;;     CLIPS Version 6.0 Example
;;;
;;;     To execute, merely load, reset and run.
;;;======================================================

; **************
; FACT TEMPLATES
; **************

; The fase fact indicates the current action to be
; undertaken before the game can begin

; (fase 
;    <action>)      ; Either elegirJugador or 
                    ; seleccionarTamanioPila

; The jugador-select fact contains the human's response to 
; the "Who moves first?" question.

; (jugador-select
;    <eleccion>)      ; Valid responses are c (for computer)
                    ; and h (for human).
    
; The seleccionarPila fact contains the human's response to 
; the "How many sticks in the pila?" question.

; (seleccionarPila
;    <eleccion>)      ; Valid responses are integers 
                    ; greater than zero.

; The tamanioPila fact contains the current numero 
; of sticks in the stack.

; (tamanioPila
;    <sticks>)      ; An integer greater than zero.

; The moverJugador fact indicates whose turn it is.

; (moverJugador
;    <jugador)       ; Either c (for computer) 
                    ; or h (for human).

; The human-takes fact contains the human's response to 
; the "How many sticks do you wish to take?" question.


; (human-takes
;    <eleccion>)      ; Valid responses are 1, 2, and 3.

; The cogerPalos facts indicate how many sticks the 
; computer should take based on the remainder when the 
; stack tamanio is divided by 4.

(deftemplate cogerPalos
   (slot cuantos)         ; Number of sticks to take.
   (slot for-remainder))   ; Remainder when stack is
                           ; divided by 4.

; ********
; DEFFACTS 
; ********

(deffacts faseInicial
   (fase elegirJugador))

(deffacts cogerPalosInformacion
   (cogerPalos (cuantos 1) (for-remainder 1))
   (cogerPalos (cuantos 1) (for-remainder 2))
   (cogerPalos (cuantos 2) (for-remainder 3))
   (cogerPalos (cuantos 3) (for-remainder 0)))

; ************
; DEFFUNCTIONS
; ************

(deffunction preguntaInicialOtraVez()
  (printout t "Jugar otra vez? (s/n) ")
  (if (eq (read) s) then
    (assert (fase elegirJugador))))

; *****
; RULES 
; *****

; RULE jugador-select
; IF
;   The fase is to choose the first jugador
; THEN
;   Ask who should mover first, and
;   Get the human's response

(defrule jugador-select
   (fase elegirJugador)
   =>
   (printout t "Quien mueve primero(Ordenador: o; Humano: h)? ")
   (assert (seleccionarJugador (read))))

; RULE good-jugador-eleccion
; IF
;   The fase is to choose the first jugador, and
;   The human has given a valid response
; THEN
;   Remove unneeded information, and 
;   Indicate whose turn it is, and
;   Indicate that the pila tamanio should be chosen

(defrule buenaEleccionJugador
   ?fase <- (fase eleccionJugador)
   ?eleccion <- (seleccionarJugador ?jugador&:(or (eq ?jugador c) (eq ?jugador h)))
   =>
   (retract ?fase ?eleccion)
   (assert (movimientoJugador ?jugador))
   (assert (fase seleccionarTamanioPila)))

; RULE bad-jugador-eleccion
; IF
;   The fase is to choose the first jugador, and
;   The human has given a invalid response
; THEN
;   Remove unneeded information, and 
;   Indicate that the first jugador should be chosen again, 
;   and Print the valid choices

(defrule malaEleccionJugador
   ?fase <- (fase eleccionJugador)
   ?eleccion <- (seleccionarJugador ?jugador&~c&~h)
   =>
   (retract ?fase ?eleccion)
   (assert (fase elegirJugador))
   (printout t "Elegir o, h." crlf))

; RULE seleccionarPila
; IF
;   The fase is to choose the pila tamanio
; THEN
;   Ask what the pila tamanio should be, and
;   Get the human's response

(defrule seleccionarPila 
   (fase seleccionarTamanioPila)
	=>
   (printout t "Cuantos palos hay en la pila? ")
   (assert (seleccionarPila (read))))

; RULE good-pila-eleccion
; IF
;   The fase is to choose the pila tamanio, and
;   The human has given a valid response
; THEN
;   Remove unneeded information, and 
;   Store the pila tamanio

(defrule buenaEleccionPila
   ?fase <- (fase seleccionarTamanioPila)
   ?eleccion <- (seleccionarPila ?tamanio&:(integerp ?tamanio)
                                &:(> ?tamanio 0))
   =>
   (retract ?fase ?eleccion)
   (assert (tamanioPila ?tamanio)))

; RULE malaEleccionPila
; IF
;   The fase is to choose the pila tamanio, and
;   The human has given a invalid response
; THEN
;   Remove unneeded information, and 
;   Indicate that the pila tamanio should be chosen again, 
;   and Print the valid choices

(defrule malaEleccionPila
   ?fase <- (fase seleccionarTamanioPila)
   ?eleccion <- (seleccionarPila ?tamanio&:(or (not (integerp ?tamanio)) (<= ?tamanio 0)))
   =>
   (retract ?fase ?eleccion)
   (assert (fase seleccionarTamanioPila))
   (printout t "Elegir un entero mayor que cero." crlf))

; RULE pierdeOrdenador
; IF
;   The pila tamanio is 1, and
;   It is the computer's mover
; THEN
;   Print that the computer has lost the game

(defrule pierdeOrdenador
  ?pila <- (tamanioPila 1)
  ?mover <- (moverJugador c)
   =>
   (printout t "El ordenador debe coger el ultimo palo!" crlf)
   (printout t "Perdi!" crlf)
   (retract ?pila)
   (retract ?mover)
   (preguntaInicialOtraVez))

; RULE pierdeHumano
; IF
;   The pila tamanio is 1, and
;   It is the human's mover
; THEN
;   Print that the human has lost the game

(defrule pierdeHumano
	?pila <- (tamanioPila 1)
	?mover <- (moverJugador h)
		=>
	(printout t "Debes coger el ultimo palo!" crlf)
	(printout t "Perdiste!" crlf)
	(retract ?pila)
	(retract ?mover)
	(preguntaInicialOtraVez))

; RULE getMoverHumano
; IF
;   The pila tamanio is greater than 1, and
;   It is the human's mover
; THEN
;   Ask how many sticks to take, and
;   Get the human's response

(defrule getMoverHumano
	(tamanioPila ?tamanio&:(> ?tamanio 1))
	(moverJugador h)
		=>
	(printout t "Cuantos palos deseas coger? ")
	(assert (cogerHumano (read))))

; RULE buenMovimientoHumano
; IF
;   There is a pila of sticks, and
;   The human has chosen how many sticks to take, and
;   It is the human's mover, and
;   The human's eleccion is valid
; THEN
;   Remove unneeded information, and 
;   Compute the new pila tamanio, and
;   Update the pila tamanio, and
;   Print the numero of sticks left in the stack, and
;   Trigger the computer jugador's turn

(defrule buenMovimientoHumano
   ?pila <- (tamanioPila ?tamanio)
   ?mover <- (cogerHumano ?eleccion)
   ?aQuienLeToca <- (moverJugador h)
   (test (and (integerp ?eleccion)
              (>= ?eleccion 1) 
              (<= ?eleccion 3)
              (< ?eleccion ?tamanio)))
   =>
   (retract ?pila ?mover ?aQuienLeToca)
   (bind ?nuevoTamanio (- ?tamanio ?eleccion))
   (assert (tamanioPila ?nuevoTamanio))
   (printout t ?nuevoTamanio " palo(s) quedan en la pila."
               crlf)
   (assert (moverJugador c)))

; RULE bad-human-mover
; IF
;   There is a pila of sticks, and
;   The human has chosen how many sticks to take, and
;   It is the human's mover, and
;   The human's eleccion is invalid
; THEN
;   Print the valid choices, and
;   Remove unneeded information, and
;   Retrigger the human jugador's mover

(defrule malMovimientoHumano
   (tamanioPila ?tamanio)
   ?mover <- (cogerHumano ?eleccion)
   ?aQuienLeToca <- (moverJugador h)
   (test (or (not (integerp ?eleccion)) 
             (< ?eleccion 1) 
             (> ?eleccion 3)
             (>= ?eleccion ?tamanio)))
   =>
   (printout t "Numero de palos debe estar entre 1 y 3,"
               crlf
               "y estas forzado a coger el ultimo palo." crlf)
   (retract ?mover ?aQuienLeToca)
   (assert (moverJugador h)))

; RULE moverOrdenador
; IF
;   It is the computers's mover, and
;   The pila tamanio is greater than 1, and
;   The computer's response is available
; THEN
;   Remove unneeded information, and
;   Compute the new pila tamanio, and
;   Print the numero of sticks left in the stack, and
;   Update the pila tamanio, and
;   Trigger the human players mover

(defrule moverOrdenador
   ?aQuienLeToca <- (moverJugador c)
   ?pila <- (tamanioPila ?tamanio&:(> ?tamanio 1))
   (cogerPalos (cuantos ?numero)
                (for-remainder ?X&:(= ?X (mod ?tamanio 4))))
   =>
   (retract ?aQuienLeToca ?pila)
   (bind ?nuevoTamanio (- ?tamanio ?numero))
   (printout t "El ordenador coge " ?numero " palo(s)."
               crlf)
   (printout t ?nuevoTamanio " palo(s) quedan en la pila."
               crlf)
   (assert (tamanioPila ?nuevoTamanio))
   (assert (moverJugador h)))

(reset)
(run)
